<?php
class BaseCmsWidget extends CWidget {
    // widget Id in the database
    protected $widgetId;
    // page_widget Id in the database
    protected $pageWidgetId;
    // page Id in the database
    protected $pageId;
    // Id of the content_type whose data is displayed on pageWidgetId
    protected $contentTypeId;
    /**
    * ContentType used by this widget
    * 
    * @var CActiveRecord
    */
    protected $contentType;
    
    /**
    * Content types that this widget can work with.
    * 
    * if null it works with any content type.
    * if false it does not work with any content type
    * if array('path to model class','...') then only work with the listed content type
    * 
    * At the first step of widget creation, widget's controller will look at this value
    * to know if it should let user choose a content type before continueing
    * 
    * @var mixed
    */
    public $supportedModels = null;
    
    /**
    * Layout used to display content
    * @var BaseCmsWidgetLayout
    */
    protected $selectedLayout;
    protected $selectedLayoutParams;
    
    /**
    * Information about the criteria which is used when query data for the widget
    * 
    * @var mixed Could be an array or JSON string
    */
    protected $dataSettings = null;
    
    /**
    * Properties what can be modified using widget preference screen
    * 
    * @var array
    */
    protected $properties = array();
    
    public function __construct($owner = null){
        $properties = array_keys($this->settingParams);
        foreach($properties as $prop)
            $this->properties[$prop] = null;
        $this->properties['Layout'] = null;

        parent::__construct($owner);
    }
    
    public function __set($name, $value) {
        if (array_key_exists($name, $this->properties))
            $this->properties[$name] = $value;
        else
            parent::__set($name, $value);
    }
    
    public function __get($name) {
        if (array_key_exists($name, $this->properties))
            return $this->properties[$name];
        if (isset($this->selectedLayout) && $this->selectedLayout->hasParam($name))
            return $this->selectedLayout->$name;
        
        return parent::__get($name);
    }
    
    /**
    * List of layouts can be used
    * This array, if not empty, will be converted to a Layout setting parameter
    * 
    * @var array associative array of 'LayoutFileName' => 'Layout title|description'
    */
    protected $layouts = array();
        
//    public function setContent($value) {
//        throw new Exception('Class '.get_class($this).' must implement function setContent($value).');
//    } 
    
    public function setPageWidgetId($value){
        $this->pageWidgetId = $value;
    }
    
    public function setPageId($value){
        $this->pageId = $value;
    }
    
    public function setDataSettings($value){
        $this->dataSettings = $value;
    }
    
    public function setContentTypeId($value){
        $this->contentTypeId = $value;
        $this->contentType = ContentType::model()->findByPk($this->contentTypeId);
    }
    
    /**
    * Content type of data the widget displays
    * 
    * @return ContentType extends CActiveRecord
    */
    public function getContentType(){
        return $this->contentType;
    }
    
    /**
    * Yii path to the model class of the content type
    * @return string
    */
    public function getContentModel(){
        if (($model = $this->getContentType()) != null)
            return $model->model;
        else
            return null;
    }
    
    /**
    * Module owns the widget
    * @return string
    */
    public function getModuleId(){
        $class=new ReflectionClass(get_class($this));
        $path = explode(DIRECTORY_SEPARATOR, dirname($class->getFileName()));
        // it is faster to do a search from the widget's folder up to root
        $path = array_reverse($path);
        $modules = array_keys(Yii::app()->modules);
        foreach($path as $folder)
            if (array_search($folder, $modules) !== false)
                return $folder;
    }
    
    /**
    * Yii path of the widget
    * @return string
    */
    public function getClassPath(){
        return $this->getModuleId().'.components.widgets.'.get_class($this);
    }
    
    /**
    * Get available layouts for the widget
    * @return Array CmsWidgetLayout objects
    */
    public function getAvailableLayouts() {
        // todo: create $this->availableLayouts, query dbase only if it's null
        $dbWidget = CmsWidget::model()->find("path = :path", array(':path' => $this->getClassPath()));
        return $dbWidget->layouts;
    }
    
    /**
    * The layout used to displayed data
    * @return
    */
    public function getSelectedLayout() {
        if (!$this->selectedLayout) {
            if ($this->Layout === null)
                return null;
                
            $dbLayout = CmsWidgetLayout::model()->findByPk($this->Layout);
            if (!$dbLayout) {
                if (count($this->getAvailableLayouts()))
                    $dbLayout = reset($this->getAvailableLayouts());
            }
            
            if (!$dbLayout)
                throw new CHttpException(500, Yii::t('Cms.Widget','Cannot find layout to display content for this widget.'));
            
            $this->selectedLayout = Yii::createComponent($dbLayout->path);
            $this->selectedLayout->owner = $this;
            $this->selectedLayoutParams = $dbLayout->params;
        }

        return $this->selectedLayout;
    }
    
    public function getSelectedLayoutParams(){
        // once the selectedLayout is set, we have the selectedLayoutSettings
        if (!$this->selectedLayout)
            $this->getSelectedLayout();
            
        return $this->selectedLayoutParams;
    }
    
    public function getActiveDataProvider(){
        // this widget does not require data from database
        if ($this->getContentModel() == null) 
            return null;
            
        // get the content
        $modelClass = Yii::createComponent($this->getContentModel());
        if (is_array($this->dataSettings))
            $criteria = new CDbCriteria($this->dataSettings);
        elseif(is_string($this->dataSettings))
            $criteria = new CDbCriteria(CJavaScript::jsonDecode($this->dataSettings));
        else
            $criteria = new CDbCriteria();
        // convert parameters base on URL's querystring values
        foreach((array)$criteria->params as $key => $value) {
            if(strpos($value, 'url:') !== false) {
                $criteria->params[$key] = Yii::app()->controller->get(substr($value,4));
            }
        }

        return new CActiveDataProvider(get_class($modelClass), array('criteria' => $criteria));        
    }
    
    /**
    * Perform a query using the dataSettings as the criteria
    * 
    * For widget that display content, this method should be 
    * called in the run() function before $this->render().
    * 
    * @return Array
    */
    public function getData(){
        return $this->getActiveDataProvider()->getData();
    }
    
    public function run(){
        if ($this->dataSettings !== null)
            $this->SelectedLayout->data = $this->getData();
        
        return $this->SelectedLayout->render();
    }
 
    /**
    * The url of page to create a new widget instance
    * 
    * @param array $params additional parameter to add to url's querystring
    * @return string
    */
    public function getCreateUrl($params = array()) {
        $className = get_class($this);
        $route = '/'.$this->getModuleId().'/widgets/'.$className.'/create';
        return url($route, $params);
    }   
    
    /**
    * The url of page to "edit" the widget instance. When widget is a listing widget
    * the edit page displays widget's preference. It's different from a view widget
    * where the edit page displays the form to edit the content item displayed by the widget.
    * 
    * @param array $params additional parameter to add to url's querystring
    * @return string
    */
    public function getEditUrl($params = array()){
        return $this->getPeferenceUrl($params);
    }
    
    /**
    * The url of page for user to customize his preferance of the widget
    * 
    * @param array $params additional parameter to add to url's querystring
    * @return string
    */
    public function getPeferenceUrl($params = array()){
        return url('/Cms/widgets/widgetBase/setting', array(
            'page_widget_id' => $this->pageWidgetId,
        ), '&', true);
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////                ////////////////////////////////////
    /////////////////////////////////   PARAMETERS   ////////////////////////////////////
    /////////////////////////////////                ////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    /**
    * Override this property to define parameter for the widget
    * 
    * @var array
    */
    protected $settingParams = array(
//            'name' => array(
//                'type' => CHtml::function or Yii path to a widget. 
                            //If CHtml function then the function name is in lower case. 
                            //Suffixes like 'field' or 'button' must be removed
//                'rules' => array('rule name - see Yii rule classes' => array(config)),
//                'items' => array(key => value) //used for dropDownList of similar CHtml functions
//                'htmlOptions' => array()
//                // following items are used to setup the widget setting info while installing  
//                'label'           => 'Short friendly param name'
//                'value'           => 'default value'
//                'description'     => 'Full description'
//                'setting_group'   => 'General Settings'
//                'ordering'        => 0
//                'visible'         => 0
//            ),
    );
    
    /**
    * Derived class for a specific widget may extend this function
    * to provide preference options for user. The returned array of
    * setting parameters should match with the widget parameters defined
    * in the __widget_setting table.
    * 
    */
    public function getSettingParams() {
        // Add the Layout parameter
        $layouts = CHtml::listData($this->getAvailableLayouts(),'id','name');
        $defaultLayout = reset(array_keys($layouts));
        $this->settingParams['Layout'] = array(
            'type' => 'dropdownlist',
            'items' => $layouts,
            'label' => 'Layout',
            'description' => 'How content is layouted and formatted',
            'setting_group' => 'General Settings',
            'visible' => count($layouts)
        );

        return $this->settingParams;
    }
    
    /**
    * Reset the widget_setting data in the database
    * Call this function with care as it will delete all user's default settings for the widget
    */
    public function resetSettingParams() {
        /**
        * Todo:
        * 1. base on the widget name, find out its ID
        * 2. use the getSettingParams() to obtain list of params whith default values
        * 3. with the widget id and param name, update the default value for each param in the widget_setting table
        */
    }
}